#import "SynchronousDataPlugin.h"
#import "BaseSynchronousData.h"

@interface SynchronousDataPlugin ()
@property (nonatomic, strong) FlutterMethodChannel *channel;
@property (nonatomic, strong) NSMutableDictionary<NSString *, SynchronousDataHandler> *handlerMaps;
@property (nonatomic, strong) NSMutableDictionary<NSString *, void(^)(NSString *, NSDictionary *data)> *flutterSyncRequests;
@end



@implementation SynchronousDataPlugin
+(instancetype)share {
    static SynchronousDataPlugin *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[SynchronousDataPlugin alloc] init];
        instance.handlerMaps = [[NSMutableDictionary alloc] init];
        instance.flutterSyncRequests = [[NSMutableDictionary alloc] init];
    });
    return instance;
}

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
    SynchronousDataPlugin* instance = [SynchronousDataPlugin share];
    instance.channel = [FlutterMethodChannel methodChannelWithName:@"synchronous_data" binaryMessenger:[registrar messenger]];
    [registrar addMethodCallDelegate:instance channel:instance.channel];
}

+(void)syncToFlutterData:(NSString *)uuid data:(NSDictionary *)data complete:(void (^)(void))complete {
    [SynchronousDataPlugin.share.channel invokeMethod:@"native_data_sync_to_flutter" arguments:@{
        @"uuid" : uuid,
        @"data" : data,
    } result:^(id  _Nullable result) {
        if(complete != nil) complete();
    }];
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
  if ([@"synchronousData" isEqualToString:call.method]) {
      
      @synchronized ([SynchronousDataPlugin share]) {
          NSString *uuid = [call.arguments valueForKey:@"uuid"];
          SynchronousDataHandler handler = [[SynchronousDataPlugin share].handlerMaps valueForKey:uuid];
          if (handler != nil) {
              BaseSynchronousData *data = handler(uuid, call.arguments[@"data"]);
              data.uuid = uuid;
          }
          result(@((BOOL)(handler != nil)));
      }
      
  } else if ([@"flutter_data_sync_to_native" isEqualToString:call.method]) {
      
      @synchronized ([SynchronousDataPlugin share]) {
          typedef void(^flutterDataHandler)(NSString *uuid, NSDictionary *data);
          NSArray<flutterDataHandler> *flutterDataSyncRequests = SynchronousDataPlugin.share.flutterSyncRequests.allValues;
          NSDictionary *arguments = (NSDictionary *)call.arguments;
          for (flutterDataHandler handler in flutterDataSyncRequests) {
              handler(arguments[@"uuid"], arguments[@"data"]);
          }
      }
      result(@(YES));
      
  } else {
      result(FlutterMethodNotImplemented);
  }
}

+(void)registSynchronousData:(NSString *)uuid handler:(SynchronousDataHandler)handler {
    @synchronized ([SynchronousDataPlugin share]) {
        [[SynchronousDataPlugin share].handlerMaps setValue:handler forKey:uuid];
    }
}

+(NSString *)receiveFlutterDataSyncRequest:(void(^)(NSString *uuid, NSDictionary *data))requestBlock {
    @synchronized (SynchronousDataPlugin.share) {
        NSString *uuid = [[NSUUID UUID] UUIDString];
        [SynchronousDataPlugin.share.flutterSyncRequests setValue:requestBlock forKey:uuid];
        return uuid;
    }
}

+(void)removeFlutterDataSyncRequest:(NSString *)flutterDataSyncRequestId {
    @synchronized (SynchronousDataPlugin.share) {
        [SynchronousDataPlugin.share.flutterSyncRequests removeObjectForKey:flutterDataSyncRequestId];
    }
}

@end

